using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace IsometricEngine {
    public static class RenderMngr {
        private const float screenWidth = 500;
        private const float screenHeight = 500;
        private const float screenForegroundMax = -100;
        private const float screenBackgroundMax = 100;

        private static Dictionary<string, int> textureIDs;
        private static Dictionary<int, Object> textureList;
        private static ContentManager contentManager;
        private static GraphicsDevice graphics;
        private static SpriteBatch spriteBatch;
        private static VertexPositionColorTexture[] vertexArray;
        private static VertexBuffer vertexBuffer;
        private static Stack<Matrix> matrixStack;

        private static BasicEffect effect;

        public static Renderable testOut;

        public static void initialize(ContentManager cM, GraphicsDevice gD, SpriteBatch sB) {
            contentManager = cM;
            graphics = gD;
            spriteBatch = sB;
            
            textureList = new Dictionary<int, object>();
            textureIDs = new Dictionary<string, int>();
            
            effect = new BasicEffect(gD);
            effect.LightingEnabled = false;
            effect.VertexColorEnabled = true;
            effect.TextureEnabled = true;
            effect.World = Matrix.Identity;
            effect.View = Matrix.CreateLookAt(new Vector3(0, 0, 0), new Vector3(0, 0, 5), Vector3.Up);
            effect.Projection = Matrix.CreateOrthographic(screenWidth, screenHeight, screenForegroundMax, screenBackgroundMax);

            loadAsset<SpriteFont>("defaultFont");
            textureIDs.Add("blank", 1);
            Texture2D tempTex = new Texture2D(gD, 1, 1);
            tempTex.SetData<Color>(new Color[] { Color.White });
            textureList.Add(1, tempTex);

            vertexArray = new VertexPositionColorTexture[4];
            vertexBuffer = new VertexBuffer(graphics, typeof(VertexPositionColorTexture), 4, BufferUsage.None);

            testOut = new Renderable();
            matrixStack = new Stack<Matrix>();
            matrixStack.Push(Matrix.Identity);
        }
        public static void uninitialize() {
            foreach (string s in textureIDs.Keys) {
                int i = textureIDs[s];
                if (typeof(Texture2D) == textureList[i])
                    ((Texture2D)textureList[i]).Dispose();
            }
            textureIDs.Clear();
            textureList.Clear();
        }

        public static int loadAsset<type>(string filename) {
            if (textureIDs.ContainsKey(filename)) return textureIDs[filename];
            int idx = textureIDs.Count();
            textureIDs.Add(filename, idx);
            textureList.Add(idx, contentManager.Load<type>(filename));
            return idx;
        }
        //TODO: public static int unloadAsset(string filename){}

        public static void renderRenderable(Renderable toRender) {
            if (toRender == null) return;
            // If not enabled, then render neither it nor its children.
            if (toRender.enabled) {
                // Render self first, if applicable
                renderTextureChildren(toRender);
                renderTextChildren(toRender);

                // Then render children
                if (toRender.children.Count() > 0) {
                    // tranformations are cumulative
                    matrixStack.Push(matrixStack.Peek() * toRender.transMatrix);
                    
                    foreach (Renderable rChild in toRender.children)
                        renderRenderable(rChild);

                    matrixStack.Pop();
                }
            }
        }
        private static void renderTextureChildren(Renderable r) {
            if (((int)r.renderType & (int)RenderType.Bitmap) == 0) return;

            vertexArray[0] = new VertexPositionColorTexture(new Vector3(r.textureSize.Left, r.textureSize.Top, r.topLeftDepth), r.tint, new Vector2(1, 1));
            vertexArray[1] = new VertexPositionColorTexture(new Vector3(r.textureSize.Right, r.textureSize.Top, r.topRightDepth), r.tint, new Vector2(0, 1));
            vertexArray[2] = new VertexPositionColorTexture(new Vector3(r.textureSize.Left, r.textureSize.Bottom, r.bottomLeftDepth), r.tint, new Vector2(1, 0));
            vertexArray[3] = new VertexPositionColorTexture(new Vector3(r.textureSize.Right, r.textureSize.Bottom, r.bottomRightDepth), r.tint, new Vector2(0, 0));

            vertexBuffer.SetData<VertexPositionColorTexture>(vertexArray);
            graphics.SetVertexBuffer(vertexBuffer);

            effect.World = r.transMatrix;

            effect.Texture = (Texture2D)textureList[r.textureID];
            graphics.SamplerStates[0] = SamplerState.LinearClamp;

            foreach (EffectPass e in effect.CurrentTechnique.Passes) {
                e.Apply();
                graphics.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);
            }
            graphics.SetVertexBuffer(null);
        }

        private static void renderTextChildren(Renderable r) {
            if (((int)r.renderType & (int)RenderType.NonIsoBitmap) != 0) {
                spriteBatch.Draw((Texture2D)textureList[r.textureID], r.textureSize, null, r.tint, 0, Vector2.Zero, SpriteEffects.None, r.textureDepth);
            }
            if (((int)r.renderType & (int)RenderType.Text) != 0) {
                spriteBatch.DrawString((SpriteFont)textureList[r.fontID], r.text, r.textPos, r.tint, 0, Vector2.Zero, 1, SpriteEffects.None, r.textDepth);
            }
        }
        public static Vector2 textSize(string text, int fontID) {
            return ((SpriteFont)textureList[fontID]).MeasureString(text);
        }
        public static Rectangle textureSize(int textureID) {
            return ((Texture2D)textureList[textureID]).Bounds;
        }
        public static Vector2 ScreenSize {
            get {
                return new Vector2(graphics.PresentationParameters.BackBufferWidth, graphics.PresentationParameters.BackBufferHeight); 
            }
        }
    }
}
